package cn.wizzer.app.web.commons.doc;

import cn.wizzer.app.bus.modules.models.BusinessFileInfo;
import cn.wizzer.app.bus.modules.services.BusinessFileInfoService;
import cn.wizzer.app.web.commons.base.Globals;
import cn.wizzer.app.web.commons.slog.annotation.SLog;
import cn.wizzer.app.web.commons.utils.DateUtil;
import cn.wizzer.app.web.commons.utils.StringUtil;
import cn.wizzer.app.web.modules.controllers.open.file.UploadController;
import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.parser.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.util.CollectionUtils;
import org.nutz.dao.Cnd;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

public class PdfUtils {
    /**
     * 通过office online word转pdf
     *
     * @param fileName  文件名称，不能带后缀
     * @param picPath  骑缝章路径
     */
    private static final Log log = Logs.get();
    @SLog(tag = "批准生成pdf", msg = "样品:${args[0]}")
    public static BusinessFileInfo wordToPdfOrQfz(String fileName, String picPath){
        BusinessFileInfo pdfReport = new BusinessFileInfo();
        try {
            log.debug("officeOnline：进入PDF 方法");
            String pdfFileName = fileName+"pdf";
            String fileNamePdf = pdfFileName+".pdf";
            //BusinessFileInfo report = businessFileInfoService.fetch(Cnd.where("delFlag", "=", "0").and("fileName", "=", fileName));
            //放到新路径吧，不要原word路径了
            String uploadPath = getUploadPathConfig();
            String date = DateUtil.format(new Date(), "yyyy") + "/" + DateUtil.format(new Date(), "MM") + "/" + DateUtil.format(new Date(), "dd");
            String filePath = "/report/" + date + "/" + fileNamePdf;
            String outputFilePath = uploadPath+filePath;

            // 指定要下载的 URL 和目标文件路径  e62bfad845cd4896a049541aad8c8d04.docx
            String wopiSrc = Globals.AppDomain+"/platform/wopi/files/"+fileName+".docx";
            String timestamp = String.valueOf(System.currentTimeMillis());
            log.debug("officeOnline：开始访问");
            String urlString = String.format(Globals.AppOfficeDomain+"/wv/WordViewer/Document.pdf?WOPIsrc=%s&z=%s&type=accesspdf",
                    java.net.URLEncoder.encode(wopiSrc, "UTF-8"), timestamp);
            // 创建 URL 对象
            URL url = new URL(urlString);
            // 打开连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(5000);  // 设置连接超时
            connection.setReadTimeout(5000);     // 设置读取超时
            //System.out.println("调用officeOnline word转pdf");
            log.debug("officeOnline：调用officeOnline word转pdf");
            // 检查响应代码
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                log.debug("officeOnline：officeOnline响应正常");
                // 获取输入流
                InputStream inputStream = new BufferedInputStream(connection.getInputStream());

                if(StringUtils.isNotEmpty(picPath)) {
                    //加盖骑缝章输出
                    stamperCheckMarkPdfStamper(inputStream, outputFilePath, picPath, "");
                }else {
                    //无骑缝章，直接输出
                    // 创建输出流
                    FileOutputStream fileOutputStream = new FileOutputStream(outputFilePath);

                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    // 将输入流中的数据写入到文件中
                    while ((bytesRead = inputStream.read(buffer)) != -1) {
                        fileOutputStream.write(buffer, 0, bytesRead);
                    }
                    // 关闭流
                    fileOutputStream.close();
                    inputStream.close();
                }
                //数据写入数据库
                pdfReport.setInfoName(pdfFileName);
                pdfReport.setInfoVersion(date);
                pdfReport.setInfoType("report");
                pdfReport.setRemark("");
                pdfReport.setOperatorName(StringUtil.getPlatformUsername());
                pdfReport.setFileType(".pdf");
                pdfReport.setDisabled(false);
                pdfReport.setFilePath(filePath);
                pdfReport.setFileName(pdfFileName);
                pdfReport.setFileBeforeName(fileNamePdf);
                pdfReport.setFileUrl(outputFilePath);
                //System.out.println("PDF 文件已成功下载并保存到: " + outputFilePath);
                log.debug("officeOnline：PDF 文件已成功下载并保存到: " + outputFilePath);
            } else {
                //System.out.println("officeOnline请求失败，HTTP 响应代码: " + responseCode);
                log.debug("officeOnline：officeOnline请求失败，HTTP 响应代码: " + responseCode);
               // System.out.println("请求失败，HTTP 响应代码: " + responseCode);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pdfReport;
    }


    public void pdfQfz()  {
        // 输出文件
        String outfilePath = "D://docs/report/2024/12/13/bbb.pdf";
        // 图片路径
        String picPath = "E://天长市项目文档/2023年天长erp/公司信息/logo2 (2).png";
        stamperCheckMarkPdf("D://docs/report/2024/12/13/e62bfad845cd4896a049541aad8c8d04.pdf", outfilePath, picPath, "");
    }

    private static String getUploadPathConfig() throws Exception {
        String uploadPath;
        //系统没开启配置,上传到项目路径
        if ("false".equals(Globals.MyConfig.getOrDefault("IsConfigUploadPath", "false"))) {
            uploadPath = UploadController.class.getClassLoader().getResource("").getPath() + "upload";
        } else if (
                ("true".equals(Globals.MyConfig.getOrDefault("IsConfigUploadPath", "false"))) && (Globals.MyConfig.getOrDefault("ConfigUploadPath", null) != null)
        ) {
            uploadPath = Globals.MyConfig.get("ConfigUploadPath").toString();
        } else {
            throw new Exception("文件上传路径配置有误");
        }
        return uploadPath;
    }
    /**
     * 盖骑缝章，通过路径
     *
     * @param infilePath  原PDF路径
     * @param outFilePath 输出PDF路径
     * @param picPath     章图片路径
     * @param keyWord     盖章关键字位置
     */
    public static void stamperCheckMarkPdf(String infilePath, String outFilePath, String picPath, String keyWord) {
        // 选择需要印章的pdf
        PdfReader reader = null;
        // 加完印章后的pdf
        PdfStamper stamp = null;
        try {
            reader = new PdfReader(infilePath);
            stamp = new PdfStamper(reader, Files.newOutputStream(Paths.get(outFilePath)));

            // 获得第一页
            Rectangle pageSize = reader.getPageSize(1);
            float height = pageSize.getHeight();
            float width  = pageSize.getWidth();

            // 获取pdf文档中的页数
            int nums = reader.getNumberOfPages();
            // 生成骑缝章切割图片
            Image[] nImage = subImages(picPath, nums);

            for(int n = 1; n <= nums; n++){
                // 设置在第几页打印印章
                PdfContentByte over = stamp.getOverContent(n);
                // 选择图片
                Image img = nImage[n-1];
                // 控制图片位置
                img.setAbsolutePosition(width-img.getWidth(),height/2-img.getHeight()/2);
                over.addImage(img);

                // 最后一页，查询盖章处，盖章
                // 存在的问题：这边我控制的是当最后一页才去根据关键字查找盖章，如果关键字出现在前两页自然找不到
                if (n == nums) {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    InputStream is = null;
                    try {
                        is = Files.newInputStream(Paths.get(infilePath));

                        byte[] buffer = new byte[is.available()];
                        int m;
                        while ((m = is.read(buffer)) != -1) {
                            bos.write(buffer, 0, m);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            bos.close();
                            if (is != null) {
                                is.close();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    // 获取输出流
                    byte[] bytes = bos.toByteArray();
                    // 获取关键字位置
                    List<Map<String, Object>> wordsCoordinates = getWordsCoordinate(bytes, keyWord);

                    if (!CollectionUtils.isEmpty(wordsCoordinates)) {
                        Map<String, Object> map = wordsCoordinates.get(wordsCoordinates.size() - 1);
                        float x = (float) map.get("x");
                        float y = (float) map.get("y");
                        float w = (float) map.get("fontWidth") * 6;
                        float h = (float) map.get("fontHeight");

                        Image[] nImage2 = subImages(picPath, 1);
                        Image img2 = nImage2[0];
                        float xx = x + w - img2.getWidth() / 2;
                        float yy = y - img2.getHeight() / 2;

                        // 控制图片位置
                        img2.setAbsolutePosition(xx, yy);
                        over.addImage(img2);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (null != stamp) {
                    stamp.close();
                }
                if (null != reader) {
                    reader.close();
                }
            } catch (Exception ignored) {}
        }
    }

    /**
     * 盖骑缝章,传入流，返回流
     *
     * @param inputStream  pdf
     * @param outFilePath 输出PDF路径
     * @param picPath     章图片路径
     * @param keyWord     盖章关键字位置
     */
    public static void stamperCheckMarkPdfStamper(InputStream inputStream,String outFilePath,String picPath,String keyWord) {
        // 选择需要印章的pdf
        PdfReader reader = null;
        // 加完印章后的pdf
        PdfStamper stamp = null;
        ByteArrayOutputStream bos = null;
        try {
            reader = new PdfReader(inputStream);
            stamp = new PdfStamper(reader, Files.newOutputStream(Paths.get(outFilePath)));
            // 获得第一页
            Rectangle pageSize = reader.getPageSize(1);
            float height = pageSize.getHeight();
            float width  = pageSize.getWidth();
            // 获取pdf文档中的页数
            int nums = reader.getNumberOfPages();
            // 生成骑缝章切割图片
            Image[] nImage = subImages(picPath, nums);
            for(int n = 1; n <= nums; n++){
                // 设置在第几页打印印章
                PdfContentByte over = stamp.getOverContent(n);
                // 选择图片
                Image img = nImage[n-1];
                // 控制图片位置
                img.setAbsolutePosition(width-img.getWidth(),height/2-img.getHeight()/2);
                over.addImage(img);

                if(StringUtils.isNotEmpty(keyWord)) {//如果根据关键字
                    // 最后一页，查询盖章处，盖章
                    // 存在的问题：这边我控制的是当最后一页才去根据关键字查找盖章，如果关键字出现在前两页自然找不到
                    if (n == nums) {
                        bos = new ByteArrayOutputStream();
                        byte[] buffer = new byte[inputStream.available()];
                        int m;
                        while ((m = inputStream.read(buffer)) != -1) {
                            bos.write(buffer, 0, m);
                        }
                        // 获取输出流
                        byte[] bytes = bos.toByteArray();
                        // 获取关键字位置
                        List<Map<String, Object>> wordsCoordinates = getWordsCoordinate(bytes, keyWord);
                        if (!CollectionUtils.isEmpty(wordsCoordinates)) {
                            Map<String, Object> map = wordsCoordinates.get(wordsCoordinates.size() - 1);
                            float x = (float) map.get("x");
                            float y = (float) map.get("y");
                            float w = (float) map.get("fontWidth") * 6;
                            float h = (float) map.get("fontHeight");

                            Image[] nImage2 = subImages(picPath, 1);
                            Image img2 = nImage2[0];
                            float xx = x + w - img2.getWidth() / 2;
                            float yy = y - img2.getHeight() / 2;
                            // 控制图片位置
                            img2.setAbsolutePosition(xx, yy);
                            over.addImage(img2);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != bos) {
                    bos.close();
                }
                if (null != stamp) {
                    stamp.close();
                }
                if (null != reader) {
                    reader.close();
                }
                if (null != inputStream) {
                    inputStream.close();
                }
            } catch (Exception ignored) {}
        }
    }

    /**
     * 切割图片
     *
     * @param imgPath 原始图片路径
     * @param n       切割份数
     * @return itextPdf的Image[]
     */
    public static Image[] subImages(String imgPath, int n) throws IOException, BadElementException {

        Image[] nImage = new Image[n];

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // 服务器测试和resource下测试
        File imgf = new File(imgPath);
        InputStream inputStream = new FileInputStream(imgf);
        // 使用本地图片测试方法
        // File resource = new File(imgPath);
        // InputStream inputStream = Files.newInputStream(resource.toPath());
        BufferedImage img = ImageIO.read(inputStream);

        // 图片高
        int h = img.getHeight();
        // 图片宽
        int w = img.getWidth();

        int sw = w / n;
        for(int i = 0; i < n; i++){
            BufferedImage subImg;
            if( i == n - 1){
                // 最后剩余部分
                subImg = img.getSubimage(i * sw, 0, w-i*sw, h);
            }else {
                // 前n-1块均匀切
                subImg = img.getSubimage(i * sw, 0, sw, h);
            }
            ImageIO.write(subImg, imgPath.substring(imgPath.lastIndexOf('.')+1),out);
            nImage[i] = Image.getInstance(out.toByteArray());
            out.flush();
            out.reset();
        }
        return nImage;
    }


    /**
     * 获取关键词位置
     *
     * @param pdfData pdf二进制字节流
     * @param keyWord 关键字
     * @return List<Map<String, Object>>
     */
    public static List<Map<String, Object>> getWordsCoordinate(byte[] pdfData, String keyWord) {

        if (StringUtils.isBlank(keyWord)) {
            return null;
        }

        List<Map<String, Object>> result = new ArrayList<>();
        PdfReader reader = null;

        try {
            // pdfData ：可以是二进制，也可以是文件路径，两种方式选择一种
            reader = new PdfReader(pdfData);
            // 获取pdf页数
            int pages = reader.getNumberOfPages();
            for (int pageNum = 1; pageNum <= pages; pageNum++) {
                // 每页的宽度
                float width = reader.getPageSize(pageNum).getWidth();
                // 每页的高度
                float height = reader.getPageSize(pageNum).getHeight();

                RenderListenerHelper renderListenerHelper = new RenderListenerHelper(pageNum, width, height);

                // 解析pdf，定位位置
                PdfContentStreamProcessor processor = new PdfContentStreamProcessor(renderListenerHelper);
                PdfDictionary pageDic = reader.getPageN(pageNum);
                PdfDictionary resourcesDic = pageDic.getAsDict(PdfName.RESOURCES);
                processor.processContent(ContentByteUtils.getContentBytesForPage(reader, pageNum), resourcesDic);
                // 文本内容
                String content = renderListenerHelper.getContent();
                // 文本每个字对应的坐标
                List<Map<String, Object>> charPositions = renderListenerHelper.getCharPositions();
                for (int i = 0; i < content.length(); i++) {
                    // 获取关键字所在位置
                    int keyIndex = content.indexOf(keyWord, i);
                    if (keyIndex == -1) {
                        break;
                    }
                    result.add(charPositions.get(keyIndex));
                    i = keyIndex + 1;
                }
            }
        } catch (Exception e) {
            //log.error("获取pdf关键字坐标失败：", e);
        } finally {
            reader.close();
        }
        return result;
    }


    static class RenderListenerHelper implements RenderListener {

        private final int pageNum;

        private final float pageWidth;

        private final float pageHeight;

        private final StringBuilder contentBuilder = new StringBuilder();

        private final List<Map<String, Object>> charPositions = new ArrayList<>();

        public RenderListenerHelper(int pageNum, float pageWidth, float pageHeight) {
            this.pageNum = pageNum;
            this.pageWidth = pageWidth;
            this.pageHeight = pageHeight;
        }

        public String getContent() {
            return contentBuilder.toString();
        }

        public List<Map<String, Object>> getCharPositions() {
            return charPositions;
        }

        // step 2 遇到"BT"执行
        @Override
        public void beginTextBlock() {

        }

        // step 3 文字主要处理方法
        @Override
        public void renderText(TextRenderInfo renderInfo) {

            //获取文本内容每个字信息集合
            List<TextRenderInfo> characterRenderInfos = renderInfo.getCharacterRenderInfos();

            for (TextRenderInfo textRenderInfo : characterRenderInfos) {
                String word = textRenderInfo.getText();
                if (word.length() > 1) {
                    word = word.substring(word.length() - 1);
                }

                // 关键字上边缘坐标
                // Rectangle2D.Float boundingRectange = textRenderInfo.getAscentLine().getBoundingRectange();
                // 关键字标准坐标(中间)
                Rectangle2D.Float boundingRectange = textRenderInfo.getBaseline().getBoundingRectange();
                // 关键字下边缘坐标
                // Rectangle2D.Float boundingRectange = textRenderInfo.getDescentLine().getBoundingRectange();
                // 正常坐标
                Float x = boundingRectange.x;
                Float y = boundingRectange.y;

                Map<String, Object> coordinate = new HashMap<>(8);

                coordinate.put("x", x);
                coordinate.put("y", y);
                // 页数
                coordinate.put("pageNum", pageNum);
                // 字体长度
                coordinate.put("fontWidth", boundingRectange.width);
                // 字段高度
                coordinate.put("fontHeight", boundingRectange.height);
                charPositions.add(coordinate);
                contentBuilder.append(word);
            }
        }

        // step 4(最后执行的，只执行一次)，遇到“ET”执行
        @Override
        public void endTextBlock() {

        }

        // step 1(图片处理方法)
        @Override
        public void renderImage(ImageRenderInfo renderInfo) {

        }
    }

}
